Skip to content

fix: guard normalizeIP against undefined or non-string IP - #472

Open
TalhaHunter101 wants to merge 1 commit into
fastify:mainfrom
TalhaHunter101:fix/normalize-ip-undefined
Open

TalhaHunter101 wants to merge 1 commit into
fastify:mainfrom
TalhaHunter101:fix/normalize-ip-undefined

Conversation

@TalhaHunter101

Copy link
Copy Markdown

Fixes #471

What kind of change does this PR introduce?

  • Bug fix
  • Feature
  • Documentation update
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

Description

In index.js, normalizeIP assumed that ip is always a valid string. When req.ip is undefined (which can occur on Unix domain sockets where socket.remoteAddress is undefined, mock/injected requests, or aborted connections):

  • !isIPv6(undefined) evaluated to true.
  • ip.toLowerCase() threw TypeError: Cannot read properties of undefined (reading 'toLowerCase').
  • Fastify crashed the incoming request with an unhandled 500 error.

This PR:

  • Adds an early check if (typeof ip !== 'string') return '' in normalizeIP so missing or non-string IPs safely return an empty string key rather than throwing.
  • Adds unit and integration tests verifying that normalizeIP(undefined) and requests with req.ip === undefined are handled gracefully without errors.

Checklist

  • run npm test
  • tests and/or benchmarks are included
  • commit message and code follows the Developer's Certification of Origin (DCO)
  • code follows the Code of Conduct

Signed-off-by: Talha Shafeeq <talha.shafeeq.ts@gmail.com>
@SameRainbows

Copy link
Copy Markdown

Tested locally on Node 22 and verified across the test suite.

Key points verified:

  • In Fastify, req.ip can resolve to undefined when operating over Unix domain sockets, custom IPC transports, or synthetic test injections. Previously, !isIPv6(undefined) evaluated to true, causing undefined.toLowerCase() to throw TypeError: Cannot read properties of undefined (reading 'toLowerCase') and returning an unhandled 500.
  • Guarding with if (typeof ip !== 'string') return '' at the top of normalizeIP safely catches undefined, null, or non-string inputs and returns a safe fallback key.
  • Verified test coverage in test/ip-normalization.test.js covering helper invocation and end-to-end route injection with an undefined req.ip. All 16 CI checks on Node 20 through 26 pass.

@Puppo Puppo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, LGTM — thanks for the fix! One non-blocking test suggestion below.

t.assert.deepStrictEqual(res1.statusCode, 200)

const res2 = await fastify.inject({ method: 'GET', url: '/' })
t.assert.deepStrictEqual(res2.statusCode, 200)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Could we also send a third request and assert 429 (updating the assertion plan)? The two 200 responses cover the crash fix; the third would verify that requests without an IP still share an enforced rate limit. I checked this locally and the current implementation returns 200, 200, 429.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

normalizeIP throws TypeError when req.ip is undefined

3 participants